<?php
	/**
	 * grids generator for the application
	 *
	 * powerful instrument for creating multi-functional grids
	 * NOTE: for multi level grids and functions use 'parent_id' field to identify the id of the parent element.
	 * @package Helpers
	 * @author Perkin Anton
	 * @version 2.1
	 */
	class GridsHelper extends HtmlxHelper
	{
		/**
		 * Default constructor
		 *
		 */
		function __construct()
		{
			global $current_controller;
			$current_controller->javascript[] = 'grids';
		}

		/**
		 * grid column number
		 *
		 * @var integer
		 */
		public $columnNumber = 0;

		/**
		 * Defines whether the filter data is taken from cache
		 *
		 * @var boolean
		 */
		private $filterCached = false;

		/**
		 * Get parameters which should not be used in the further links (they are actual only on the current page)
		 *
		 * @var array
		 */
		private $escapeGetParameters = array('filterCached');

		/**
		 * defines whether to use Live (JavaScript) Filter or not
		 *
		 * @var boolean
		 */
		public $useLiveFilter = true;

		/**
		 * defines whether edit mode is on/off
		 *
		 * @var boolean
		 */
		public $isEditMode = false;

		/**
		 * array with the content of the grid header.
		 *
		 * @var array
		 */
		private $gridHeader = array();

		/**
		 * 2-dimentional array (rows/columns) with the content of the grid. Each element represents only text (without any tags, etc) data of a cell
		 *
		 * @var array
		 */
		private $gridTextContent = array();

		/**
		 * 2-dimentional array (rows/columns) with the content of the grid. Each element represents data of a cell
		 *
		 * @var array
		 */
		private $gridContent = array();

		/**
		 * Grid totals by columns
		 *
		 * @var array
		 */
		private $gridTotals = array();

		/**
		 * Grid subtotals by columns
		 *
		 * @var array
		 */
		private $gridSubTotals = array();

		/**
		 * Grid subtotals
		 *
		 * @var array
		 */
		private $subTotals = array();

		/**
		 * CSS class for the grid
		 */
		public $gridCssClass = 'ui-widget ui-widget-content grid';

		/**
		 * CSS class for the grid cell
		 */
		public $gridCellCssClass = 'gridCell';

		/**
		 * CSS class for the grid cell in grid totals
		 */
		public $gridTotalsCellCssClass = 'gridCellTotals';

		/**
		 * CSS class for the grid cell label in grid totals
		 */
		public $gridTotalsLabelCssClass = 'gridLabelTotals';

		/**
		 * CSS class for the odd grid row
		 */
		public $gridRowOddCssClass = 'gridRow';

		/**
		 * CSS class for the even grid row
		 */
		public $gridRowEvenCssClass = 'gridRow';

		/**
		 * CSS class for the totals grid row
		 */
		public $gridTotalsRowCssClass = 'gridRowTotals';

		/**
		 * CSS class for the grid header row
		 */
		public $gridHeaderRowCssClass = 'gridHeader ui-widget-header';

		/**
		 * CSS class for the grid header cell
		 */
		public $gridHeaderCellCssClass = 'gridHeader';

		/**
		 * CSS class for the grid sort cell
		 */
		public $gridSortCellCssClass = 'gridSort';

		/**
		 * CSS class for the grid legend table
		 */
		public $legendTableCssClass = 'eva';

		/**
		 * CSS class for the submit/search/cancel buttons
		 */
		public $submitButtonCssClass = 'submit';

		/**
		 * URL for the mass delete operation
		 */
		public $massDeleteOperationUrl = '/main/massOperations/';

		/**
		 * URL for the edit mode
		 */
		public $editModeFormUrl = '/main/savegrid/';

		/**
		 * combobox CSS class
		 */
		public $comboboxCssClass = 'combobox';

		/**
		 * Large paste image name
		 */
		public $pasteImageLarge = 'btnPasteLarge.gif';

		/**
		 * Large paste image name
		 */
		public $addImageLarge = 'btnNew.gif';

		/**
		 * Large edit image name
		 */
		public $editImageLarge = 'btnEditLarge.gif';

		/**
		 * Large view image name
		 */
		public $viewImageLarge = 'btnViewLarge.gif';

		/**
		 * Plus image name
		 */
		public $plusImageName = 'plus.gif';

		/**
		 * Plus minus name
		 */
		public $minusImageName = 'minus.gif';

		/**
		 * Plus/minus (actually, no image) image name
		 */
		public $plusminusImageName = 'plusminus.gif';

		/**
		 * arrow up (for the sorting) image name
		 */
		public $arrowUpImageName = 'arrow_up.gif';

		/**
		 * arrow up (for the sorting) image name
		 */
		public $arrowDownImageName = 'arrow_down.gif';

		/**
		 * CSS class for the pagination table
		 */
		public $paginationTableCssClass = 'pagination';

		/**
		 * CSS class for the pagination cell
		 */
		public $paginationCellCssClass = 'pagination';

		/**
		 * CSS class for the pagination link
		 */
		public $paginationLinkCssClass = 'pagination';

		/**
		 * CSS class for the filter table
		 */
		public $filterTableCssClass = 'filter';

		/**
		 * CSS class for the filter header cell
		 */
		public $filterHeaderCellCssClass = 'filterHeader';

		/**
		 * CSS class for the filter label cell
		 */
		public $filterLabelCellCssClass = 'filterLabel';

		/**
		 * CSS class for the filter alphabetic unit
		 */
		public $filterAlphabeticUnit = 'alphabeticUnit';

		/**
		 * CSS class for the input text field
		 */
		public $inputTextCssClass = 'text';

		/**
		 * CSS class for the input text field
		 */
		public $inputTextLiveFilterCssClass = 'liveFilter';

		/**
		 * CSS class for the input search field (used in fast search feature)
		 */
		public $inputTextSearchCssClass = 'search';

		/**
		 * CSS class for the checkbox field
		 */
		public $checkboxCssClass = 'checkbox';

		/**
		 * CSS class for error table cell
		 */
		public $errorTableCellCssClass = 'error';

		/**
		 * CSS class for the action buttons
		 */
		public $actionButtonCssClass = 'buttons';

		/**
		 * view (action button) image name
		 */
		public $viewImageName = 'btnView.gif';

		/**
		 * edit (action button) image name
		 */
		public $editImageName = 'btnEdit.gif';

		/**
		 * delete (action button) image name
		 */
		public $deleteImageName = 'btnDelete.gif';

		/**
		 * permissions (action button) image name
		 */
		public $permissionImageName = 'btnPermissions.gif';

		/**
		 * add child (action button) image name
		 */
		public $addChildImageName = 'btnAddChild.gif';

		/**
		 * cut image name
		 */
		public $cutImageName = 'btnSmallCut.gif';

		/**
		 * paste inactive image (for the cut/paste operation). Active image name should be the same, but without 'Inactive' (is is a strict requirement).
		 */
		public $pasteInactiveImageName = 'btnPasteSmallInactive.gif';


		# GRIDS #
	    /**
		 * Generates simple grid
		 *
		 * @param array $data the text to display
  		 * @param array $fields array with the fields to display
  		 * @param boolean $use_filter indicates whether to display filter box or not (default: 0)
  		 * @param boolean $use_actions indicates whether to display action button group or not (default: 1)
  		 * @param boolean $use_sort indicates whether to use sorting or not (default: 1)
  		 * @param array $grid_settings grid setting array. Detailed information on its elements can be found in Guide section.
		*/
		public function Grid($data, $fields, $use_filter = 0, $use_actions = 1, $use_sort = 1, $grid_settings = null)
		{
			if ($grid_settings == null) $grid_settings = array();

			//defines the grid name
			if (!isset($grid_settings[name])) $grid_settings[name] = 'grid';

			Logger::Debug("Grid ({$grid_settings[name]}) started");

			global $current_controller;

			//check whether to use filter
			if ($use_filter) $this->Filter($fields, null, $grid_settings);

			//check whether to use alphabetic filter
			if (!empty($grid_settings['alphabetic_filter'])) $this->AlphabeticFilter($grid_settings['alphabetic_filter']);

			//if now records
			if (empty($data))
			{
				$this->NoRecords();
				return;
			}

			echo "<table border='0' cellpadding = '0' cellspacing = '0' width='100%'>";
			if (!empty($current_controller->data['num_rows']))
			{
				echo "<tr><td align='right'>";

				//pagination table
				$this->Pagination($current_controller->data[num_rows], $current_controller->request->get[page_number], $current_controller->request->get[page]);
				echo "</td></tr>";
			}

			echo "<tr><td>";

			$grid_buttons = array();
			if ((!isset($grid_settings[use_print]))||($grid_settings[use_print] == true))
			{
				//grid print button
				$grid_buttons['print'] = array
				(
					'image' => $this->addImageLarge,
					'text' => LANG_PRINT,
					'attributes' => "id = '$grid_settings[name]_print' onclick = 'this.href = this.href + \"&selected=\" + GetSelectedRowsString(\"chk\", \"$grid_settings[name]\");'",
					'url' => ''
				);
			}

			//grid export to Excel button
			if ((isset($grid_settings['use_export_excel']))||($grid_settings['use_export_excel'] == true))
			{
				//grid print button
				$grid_buttons['export_excel'] = array
				(
					'image' => $this->addImageLarge,
					'text' => LANG_EXPORT_TO_EXCEL,
					'attributes' => "id = '$grid_settings[name]_export_excel' onclick = 'this.href = this.href + \"&selected=\" + GetSelectedRowsString(\"chk\", \"$grid_settings[name]\");'",
					'url' => ''
				);
			}

			if ($grid_settings['edit_mode'] == true)
			{
				//eleminates multiple 'detailed' from url
				$_SERVER['REQUEST_URI'] = ereg_replace("(&){0,1}(editable=){1}(1){1}", '', $_SERVER['REQUEST_URI']);

				//displays the appropriate button depending on the grid mode selected
				if ($current_controller->request->get[editable])
				{
					$this->isEditMode = true;
					$this->js("jforms");

					$view_mode_link = $_SERVER['REQUEST_URI'];

					$grid_buttons['editable'] = array
					(
						'image' => $this->viewImageLarge,
						'text' => LANG_VIEW_MODE,
						'url' => $view_mode_link
					);

					unset($grid_buttons['print']);
				}
				else
				{
					$this->isEditMode = false;

					//adds '?' to the link if necessary
					if (!strstr($_SERVER['REQUEST_URI'], '?')) $_SERVER['REQUEST_URI'] = $_SERVER['REQUEST_URI'].'?';

					$edit_mode_link = $_SERVER['REQUEST_URI'].'&editable=1';

					$grid_buttons['editable'] = array
					(
						'image' => $this->editImageLarge,
						'text' => LANG_EDIT_MODE,
						'url' => $edit_mode_link
					);
				}
			}

			//grid toolbar
			if (!empty($grid_buttons))
			{
				$current_controller->helpers->toolbars->Toolbar($grid_buttons);
			}
			echo "<div id = 'grid_container' class='ui-widget' style = 'margin-top: 5px;'><table class='$this->gridCssClass' id = '$grid_settings[name]'>";

			if (!isset($grid_settings[use_mass_operations])) $grid_settings[use_mass_operations] = 1;
			else $grid_settings[use_mass_operations] = 0;

			//grid header
			$this->TableHeaderRowMultiCellsSort($fields, 0, $use_actions, $grid_settings[use_mass_operations], $use_sort, $grid_settings);

			//grid live filter
			if (($this->useLiveFilter) && (!isset($grid_settings[use_live_filter]) || $grid_settings[use_live_filter]) && (!$this->isEditMode)) $this->GridLiveFilter($fields, 0, $use_actions, $grid_settings);

			if ($this->isEditMode)
				$this->form($this->editModeFormUrl, '', 'onsubmit = "jsubmit(this, \'gridSaveResult\');return false;" id="editableForm"');
			else if ($grid_settings[use_mass_operations])
				$this->form($this->massDeleteOperationUrl, 'POST', 'onsubmit = "return confirm(\''.LANG_DO_YOU_REALLY_WANT_TO_DELETE_SELECTED_RECORDS.'\')" id="mass"');

			$this->rowNumber = 0;
			foreach ($data as $row)
			{
				//starts column counter
				$this->columnNumber = 0;

				//css classes for the row and cell
				if ($this->rowNumber%2==1) $rowStyle = $this->gridRowOddCssClass;
	    		else $rowStyle = $this->gridRowEvenCssClass;
	    		$cellStyle = $this->gridCellCssClass;

	    		//get optional row attributes
	    		$additionalRowAttributes = $this->GetRowAttributes($row, $grid_settings['conditionalRowAttributes']);

	    		//get what operations should be disabled for the given row
	    		if (!empty($grid_settings['conditionalDisableOperation']))
	    		{
	    			$disabledOperations = $this->GetDisabledOperations($row, $grid_settings['conditionalDisableOperation']);
	    		}

				echo "<tr id = '{$grid_settings[name]}_row_$this->rowNumber' class = '$rowStyle' $additionalRowAttributes>";

				unset($additionalRowAttributes);

				//displays a checkbox to the left of each row if necessary
				if ($this->isEditMode)
				{
					//edit mode code here...
				}
				else if ($grid_settings['use_mass_operations'])
				{
					//checks whether delete operation is disable for the given row
					if (!$disabledOperations[delete])
					{
						$showMassCheckBox = 1;
					}
					else
					{
						$showMassCheckBox = 0;
					}
					$this->CheckBoxCell($row, $cellStyle, $showMassCheckBox, $grid_settings);
				}

				foreach ($fields as $key=>$value)
				{
					//generates a grid cell
					$this->GridCell($key, $value, $row, &$grid_settings, $cellStyle, '', $fields);
				}

				if (!$use_actions) $hide_button_group = "style='display:none'";

				//checks whether to show 1 or more parameters in ActionButtonGroup
				$action_ids = $this->GetActionParametersString($grid_settings, $row);

					echo "<td class='$cellStyle' width='75' $hide_button_group>";
					$this->ActionButtonGroup($grid_settings, $action_ids, $current_controller->data['general']['table_id']);
					echo "</td></tr>";

				$this->rowNumber++;
			}

			//makes totals (sum, avg, etc)
			if ($grid_settings['use_totals'])
			{
				//finishes subtotals
				if (!empty($this->groupByField))
				{
					foreach ($this->gridSubTotals[$grid_settings['name']] as $k => $v)
					{
						foreach ($fields[$k]['totals'] as $total)
						{
							switch ($total)
							{
								case 'sum':
									$this->subTotals[$grid_settings['name']][$this->groupByField][$row[$this->groupByField]][$k]['sum'] = $v['sum'];
									break;
								case 'count':
									$this->subTotals[$grid_settings['name']][$this->groupByField][$row[$this->groupByField]][$k]['count'] = $v['count'];
									break;
							}
						}
					}
				}
				else
				{
					$this->subTotals = array();
				}

				$this->Totalize($fields, $grid_settings);
			}

			echo "</table></div></td></tr></table>";

			if (!empty($this->subTotals))
			{
				$this->SubTotalize($fields, $grid_settings);
			}

			if ($this->isEditMode)
			{
				$this->submit('save'.$grid_settings[name], LANG_TO_SAVE, "class = '$this->submitButtonCssClass' style = 'margin-top: 2px;'");
				echo "<div id = 'gridSaveResult'></div>";
				echo '</form>';
			}
			else if ($grid_settings[use_mass_operations])
			{
				$model_name = isset($grid_settings[controller]) ? $grid_settings[controller] : $current_controller->name;
				$this->hidden('model_name', str_replace('_', '', $model_name));
				$this->submit('delete', LANG_TO_DELETE, "class = '$this->submitButtonCssClass' style = 'margin-top: 2px;'");
				echo '</form>';
			}

			//Marks the selected row
			$this->MarkGridRow($grid_settings);

			//grid shortcuts
			$this->ShortCuts($grid_settings);

			//checks whether to show the legend
			if (!empty($grid_settings[legend]))
			{
				$this->Legend($grid_settings[legend]);
			}

			//TODO: to make it work correct + header row
			//$current_controller->ExportToSpreadsheet($this->gridContent, APP.SEPARATOR.'testx.xls');

			//generates link for the grid print preview
			Logger::Debug("Grid ({$grid_settings[name]}) ended");

			//generates link for the grid export to excel
			$this->GenerateExportToExcelLink($grid_settings[name], $grid_settings);

			//generates link for the grid print preview
			$this->GeneratePrintLink($grid_settings[name], $grid_settings);
		}

	    /**
		 * Generates grid with mass operations
		 *
		 * @param array $data the text to display
  		 * @param array $fields array with the fields to display
   		 * @param array $mass_operations array with the mass operations
   		 * @param boolean $use_filter indicates whether to display filter box or not (default: 0)
   		 * @param boolean $use_actions indicates whether to display action button group or not (default: 1)
		*/
		public function GridMassOperations($data, $fields, $mass_operations, $use_filter = 0, $use_actions = 1, $use_sort = 1, $grid_settings = null)
		{
			//defines the grid name
			if (!isset($grid_settings[name])) $grid_settings[name] = 'grid';

			Logger::Debug("Grid ({$grid_settings[name]}) started");

			global $current_controller;

			//check whether to use filter
			if ($use_filter)
			{
				$this->Filter($fields, null, $grid_settings);
			}

			//if now records
			if (empty($data))
			{
				$this->NoRecords();
				return;
			}

			//makes grid content empty
			$this->gridContent = array();

			//defines the grid name
			if (!isset($grid_settings[name])) $grid_settings[name] = 'grid';

			echo "<table border='0' cellpadding = 0 cellspacing = 0 width='100%'><tr><td align='right'>";
			$this->pagination($current_controller->data[num_rows], $current_controller->request->get[page_number], $current_controller->request->get[page]);
			echo "</td></tr><tr><td>";

			//grid toolbar
			$grid_buttons = array
			(
				'print' => array
				(
					'image' => $this->addImageLarge,
					'text' => LANG_PRINT,
					'attributes' => "id = '$grid_settings[name]_print' onclick = 'this.href = this.href + \"&selected=\" + GetSelectedRowsString(\"chk\", \"$grid_settings[name]\");'",
					'url' => ''
				),
			);

			if (isset($grid_settings[item_structure]))
			{
				//eleminates multiple 'detailed' from url
				$_SERVER['REQUEST_URI'] = ereg_replace("(&){0,1}(detailed=){1}(1){1}", '', $_SERVER['REQUEST_URI']);

				$simple_link = $_SERVER['REQUEST_URI'];

				//adds '?' to the link if necessary
				if (!strstr($_SERVER['REQUEST_URI'], '?')) $_SERVER['REQUEST_URI'] = $_SERVER['REQUEST_URI'].'?';

				$detailed_link = $_SERVER['REQUEST_URI'].'&detailed=1';

				//disolays the appropriate button depending on the grid view type selected
				if ($current_controller->request->get[detailed])
				{
					$grid_buttons[detailed] = array
					(
						'image' => $this->addImageLarge,
						'text' => LANG_SIMPLE_GRID_VIEW,
						'url' => $simple_link
					);
				}
				else
				{
					$grid_buttons[detailed] = array
					(
						'image' => $this->addImageLarge,
						'text' => LANG_DETAILED_GRID_VIEW,
						'url' => $detailed_link
					);
				}
			}

			//displays the grid toolbar
			$current_controller->helpers->toolbars->Toolbar($grid_buttons);

			$this->form('', 'POST', 'id="mass"');
			echo "<table class='$this->gridCssClass'>";

			//grid header
			if (!isset($current_controller->request->get[detailed]))
				$this->tableHeaderRowMultiCellsSort($fields, 0, 0, 1, 1, $grid_settings);

			//grid live filter
			//$this->GridLiveFilter($fields, 0, $use_actions, 1);

        	//first filter cell
        	$this->JavaScript('firstFilterCell = null; ');				
				
			//Logger::Debug("rows started");
			//rows started
			$this->rowNumber = 0;
			foreach ($data as $row)
			{
				//Logger::Debug("row #{$this->rowNumber} started");
				//css classes for the row and cell
				if ($this->rowNumber%2==1) $rowStyle = $this->gridRowOddCssClass;
	    		else $rowStyle = $this->gridRowEvenCssClass;
	    		$cellStyle = $this->gridCellCssClass;

	    		//get optional row attributes
	    		$additionalRowAttributes = $this->GetRowAttributes($row, $grid_settings[conditionalRowAttributes]);

				echo "<tr id = '{$grid_settings[name]}_row_$this->rowNumber' class = '$rowStyle' $additionalRowAttributes>";

				unset($additionalRowAttributes);

				$this->CheckBoxCell($row, $cellStyle);

				//generates cells in each row
				//Logger::Debug("cells for row #{$this->rowNumber} started");
				$this->columnNumber = 0;

				if ($current_controller->request->get[detailed])
				{
					$this->GridDetailedRow($fields, $row, $grid_settings);
				}
				else
				{
					foreach ($fields as $key=>$value)
					{
						//generates a grid cell
						$this->GridCell($key, $value, $row, &$grid_settings, $cellStyle, '', $fields, $data);
					}
				}

				//Logger::Debug("cells for row #{$this->rowNumber} ended");

				//checks if actions are used
				if (!$use_actions) $hide_button_group = "style = 'display: none'";

				//checks whether to show 1 or more parameters in ActionButtonGroup
				$action_ids = $this->GetActionParametersString($grid_settings, $row);

					echo "<td class='$cellStyle' width='75' $hide_button_group>";
					$this->ActionButtonGroup($grid_settings, $action_ids, $current_controller->data[general][table_id]);
					echo "</td>";

				echo "</tr>";

				//Logger::Debug("row #{$this->rowNumber} ended");
				$this->rowNumber++;
			}
			//Logger::Debug("rows ended");
			echo "</table></td></tr>";

			//mass operations
			echo "<tr><td><table><tr><td>";
			$this->select('mass_operation', $mass_operations, '', "class='$this->comboboxCssClass' style='width: 200px'");
			echo "</td><td>";
			$this->button('submit_mass_operation', LANG_APPLY_FOR_SELECTED, "class='$this->submitButtonCssClass' onClick='areChecked(\"chk\", \"mass\", \"".LANG_NO_RECORDS_SELECTED."\");'");
			echo "</td></tr></table></td></tr></form>";

			echo "</table>";

			//Marks the selected row
			$this->MarkGridRow($grid_settings);

			//grid shortcuts
			$this->ShortCuts($grid_settings);

			//we checks if want to show legend
			if (!empty($grid_settings[legend]))
			{
				$this->Legend($grid_settings[legend]);
			}

			//generates link for the grid print preview
			$this->GeneratePrintLink($grid_settings[name], $grid_settings);

			Logger::Debug("Grid ({$grid_settings[name]}) ended");
		}

	    /**
		 * Generates hierarchical grid
		 *
		 * @param array $data the text to display
  		 * @param array $fields array with the fields to display
  		 * @param boolean $use_filter indicates whether to display filter box or not (default: 0)
		*/
		public function GridChild($data, $fields, $use_filter=0, $use_actions = 1, $use_sort = 1, $grid_settings = null)
		{
			global $current_controller;

			//defines the grid name
			if (!isset($grid_settings[name])) $grid_settings[name] = 'grid';

			$tool_buttons = array
			(
				'paste' => array
				(
					'image' => $this->pasteImageLarge,
					'text' => LANG_PASTE,
					'attributes' => 'onClick = "SendValuesWithPageReload(\'/'.$current_controller->name.'/ClipboardPaste/0/\' + clipboard); return false;"'
				),
				'expand' => array
				(
					'image' => $this->pasteImageLarge,
					'text' => LANG_EXPAND_ALL,
					'url' => '?parent_id=-1'
				),
				'collapse' => array
				(
					'image' => $this->pasteImageLarge,
					'text' => LANG_COLLAPSE_ALL,
					'url' => '?parent_id=0'
				),
				'print' => array
				(
					'image' => $this->addImageLarge,
					'text' => LANG_PRINT,
					//'attributes' => "id = '$grid_settings[name]_print'",
					'attributes' => "id = '$grid_settings[name]_print' onclick = 'this.href = this.href + \"&selected=\" + GetSelectedRowsString(\"chk\", \"$grid_settings[name]\");'",
					'url' => ''
				)
			);
			$current_controller->helpers->toolbars->Toolbar($tool_buttons);

			//gets parents string
			$parents = $current_controller->request->get[parent_id];

			//URL index page with the get parameters (for the redirect purposes)
			$_SESSION[$current_controller->name][indexGetUrl] = '?'.$_SERVER[REDIRECT_QUERY_STRING];

			//check whether to use filter
			if ($use_filter) $this->Filter($fields, '', $grid_settings);

			//if now records
			if (empty($data))
			{
				$this->NoRecords();
				return;
			}

        	//first filter cell
        	$this->JavaScript('firstFilterCell = null; ');			
			
			if (!isset($grid_settings[use_mass_operations])) $grid_settings[use_mass_operations] = 1;
			else $grid_settings[use_mass_operations] = 0;

			//defines the grid name
			if (!isset($grid_settings[name])) $grid_settings[name] = 'grid';

			echo "<table border='0' cellpadding = 0 cellspacing = 0 width='100%'><tr><td>";
			echo "<table class='$this->gridCssClass'>";

			//grid header
			$this->TableHeaderRowMultiCellsSort($fields, 0, $use_actions, $grid_settings[use_mass_operations], $use_sort, $grid_settings);

			//for the mass operation
			if ($grid_settings[use_mass_operations])
				$this->form($this->massDeleteOperationUrl, 'POST', 'onsubmit = "return confirm(\''.LANG_DO_YOU_REALLY_WANT_TO_DELETE_SELECTED_RECORDS.'\')" id="mass"');

			//grid child row
			$this->rowNumber = 0;
			$this->GridChildRow($data, $fields, 0, $parents, 0, $use_actions, $grid_settings);

			$this->counter = 0;

			echo "</table></td></tr></table>";

			if ($grid_settings[use_mass_operations])
			{
				$this->hidden('model_name', str_replace('_', '', $current_controller->name));
				$this->submit('delete', LANG_TO_DELETE, "class = '$this->submitButtonCssClass' style = 'margin-top: 2px;'");
				echo '</form>';
			}

			//Marks the selected row
			$this->MarkGridRow($grid_settings);

			//grid shortcuts
			$this->ShortCuts($grid_settings);

			//we checks if want to show legend
			if (!empty($grid_settings[legend]))
			{
				$this->Legend($grid_settings[legend]);
			}

			//generates link for the grid print preview
			$this->GeneratePrintLink($grid_settings[name], $grid_settings);
		}

	    /**
		 * Generates hierarchical grid row
		 *
		 * @param array $data the text to display
  		 * @param array $fields array with the fields to display
  		 * @param integer $parent_id parent element id
  		 * @param string $parents string with all parent ids
  		 * @param boolean $level element level
		*/
		public function GridChildRow($data, $fields, $parent_id, $parents, $level = 0, $use_actions, $grid_settings = null)
		{
			global $current_controller;
			foreach ($data as $row)
			{
				if ($row[parent_id]!=$parent_id) continue;

				if ($parents == -1)
				{
					$parents = 0;
					$is_collapsed = true;
				}
				else $is_collapsed = false;

				//css classes for the row and cell
				if ($this->rowNumber%2==1) $rowStyle = $this->gridRowOddCssClass;
	    		else $rowStyle = $this->gridRowEvenCssClass;
	    		$cellStyle = $this->gridCellCssClass;

	    		//get optional row attributes
	    		$additionalRowAttributes = $this->GetRowAttributes($row, $grid_settings[conditionalRowAttributes]);

				echo "<tr id = '{$grid_settings[name]}_row_$this->rowNumber' class = '$rowStyle' $additionalRowAttributes>";

				unset($additionalRowAttributes);

				//displays a checkbox to the left of each row if necessary
				if ($grid_settings[use_mass_operations])
				{
					$this->CheckBoxCell($row, $cellStyle);
				}

				$fields_count = 0;
				foreach ($fields as $key=>$value)
				{
					if ((isset($value[in_grid]))&&($value[in_grid]==false)) continue;

					//eliminates row[id] in parent_id string (to collapse the list)
					$temp_array = explode(',', $parents);
					$temp = array();
					foreach ($temp_array as $k=>$v)
					{
						if (($v!=$row[id])&&($v>=0)) $temp[$k] = $v;
					}

					$parents_local = implode(',', $temp);

					//determine what sign to use next to the field name
					$parents_array = split(',', $parents);
					if ((in_array($row[id], $parents_array))||($is_collapsed))
					{
						$id = '';
						$flag = 0;
						$id_local = '';
						foreach ($data as $temp_row)
						{
							if ($temp_row[parent_id]==$row[id])
							{
								$flag=1;
								break;
							}
						}
						if ($flag == 1)
						{
							$sign='<img src='.IMAGES.SEPARATOR.$this->minusImageName.' /> ';
							$url_id = "{$grid_settings[name]}_collapse_".$this->rowNumber;
						}
						else
						{
							$sign='<img src='.IMAGES.SEPARATOR.$this->plusminusImageName.' /> ';
							$url_id = "";
						}
					}
					else
					{
						$id = ','.$row[id];
						$sign='<img src='.IMAGES.SEPARATOR.$this->plusImageName.' /> ';
						$url_id = "{$grid_settings[name]}_expand_".$this->rowNumber;
					}

					//adds '?' if necessary in the beginning of GET string
					if (!strstr($_SERVER['REQUEST_URI'], '?')) $_SERVER['REQUEST_URI'] = $_SERVER['REQUEST_URI'].'?';

					//eleminates multiple 'parent_id' from url
					$_SERVER['REQUEST_URI'] = ereg_replace("(&){0,1}(parent_id=){1}([0-9,-])*", '', $_SERVER['REQUEST_URI']);

				  	//eleminates multiple 'selectedrow' from url
				  	$_SERVER['REQUEST_URI'] = ereg_replace("(&){0,1}(selectedrow=){1}([0-9])*", '', $_SERVER['REQUEST_URI']);

		  			//counts level for outdent
					$this->count = 0;
					$this->CountLevel($data,$row[parent_id]);

					//draws plus/minus image with the appropriate link
					$before_cell='';
					if ($fields_count==0)
					{
						for ($i=0; $i<$this->count; $i++)
						{
							$before_cell .=  "&nbsp;&nbsp;&nbsp;&nbsp;";
						}

						//determine whether to display link or not
						if ($sign=='<img src='.IMAGES.SEPARATOR.$this->plusminusImageName.' /> ') $before_cell .= '<img src='.IMAGES.SEPARATOR.$this->plusminusImageName.' /> ';
						else $before_cell .= '<a href="'.$_SERVER[REQUEST_URI].'&parent_id='.$parents_local.$id.'&selectedrow='.$this->rowNumber.'" class="sign" id = "'.$url_id.'">'.$sign.'</a>';
					}

					//generates a grid cell
					$this->GridCell($key, $value, $row, $grid_settings, $cellStyle, $before_cell, $fields);

					$fields_count++;
				}

				//action button group
				if (!$use_actions) $hide_button_group = "style='display:none'";

				//checks whether to show 1 or more parameters in ActionButtonGroup
				$action_ids = $this->GetActionParametersString($grid_settings, $row);

					echo "<td class='$cellStyle' width='75' $hide_button_group>";
					$this->ActionButtonGroup($grid_settings, $action_ids, $current_controller->data[general][table_id], 1);
					echo "</td>";

				echo "</tr>";

				//next iteration
				$this->rowNumber++;

				//recurse
				if ($is_collapsed) $parents = -1;
				$this->GridChildRow($data, $fields, $row[id], $parents, $level, $use_actions, $grid_settings);
			}
		}

	    /**
		 * Generates grid with logs
		 *
		 * @param array $data the text to display
  		 * @param array $fields array with the fields to display
		 */
		public function GridLogs($data, $fields)
		{
			global $current_controller;

			if (empty($data))
			{
				$this->NoRecords();
				return;
			}

			echo "<table border='0' cellpadding = 0 cellspacing = 0 width='100%'><tr><td>";

			echo "<table cellpadding = 0 cellspacing = 1 border=0 class='$this->gridCssClass'>";

			$this->tableHeaderRowMultiCellsSort($fields,0,0,0,0);

			$i=0;
			foreach ($data as $row)
			{
				//css classes for the row and cell
				if ($this->rowNumber%2==1) $rowStyle = $this->gridRowOddCssClass;
	    		else $rowStyle = $this->gridRowEvenCssClass;
	    		$cellStyle = $this->gridCellCssClass;

				echo "<tr class = '$rowStyle'>";

				foreach ($fields as $key=>$value)
				{
					if ((isset($value[in_grid]))&&($value[in_grid]==false)) continue;

					if (isset($value[url]))
					{
						echo "<td class='$cellStyle'>";
						$this->link('/'.$value[url].'/view/'.$row[$value[id]].'/', $row[$key]);
						echo "</td>";
					}
					else
					{
						if ($current_controller->controller_model->db_structure[$key][type]=='bit')
						{
							if ($row[$key]==0) echo "<td class='$cellStyle'>".LANG_NO."</td>";
							else echo "<td class='$cellStyle'>".LANG_YES."</td>";
						}
						else
							echo "<td class='$cellStyle'>".$row[$key]."</td>";
					}
				}
				$i++;
			}
			echo "</table></td></tr></table>";

		}


		# GRID COMPONENTS #

	    /**
		 * Generates a grid cell
		 *
		 * @param array $key key of the field array
  		 * @param array $value values of the field array
  		 * @param array $row grid row
  		 * @param array $grid_settings settings for the whole grid
  		 * @param string $cellStyle cell style
  		 * @param string $before_cell somrthing to be displayed before grid cell data
  		 */
		public function GridCell($key, $value, $row, $grid_settings, $cellStyle, $before_cell = '', $fields, $data = null)
		{
			//Logger::Debug("cell for row #{$this->rowNumber} #$key started");
			if ((isset($value['in_grid']))&&($value['in_grid'] == false)) return;

			global $object, $current_controller;

			//opens cell
			if (!$grid_settings['disableRowSelection']) $onDblClick = "'SelectGridRow(\"{$grid_settings['name']}_{$object->config->defaultGridAction}_{$this->rowNumber}\")'";
			else $onDblClick = "''";

			echo "<td id = '{$key}-{$this->rowNumber}' class='$cellStyle' {$value['cellHtmlAttributes']} onDblClick = $onDblClick onMouseDown = 'MarkGridRow(\"$this->rowNumber\", \"\", \"{$grid_settings['name']}\")'>";

			//if there is grouping by this field (to calculate subtotals)
			if ($value['group_by'])
			{
				$this->groupByField = $key;

				if (($this->gridContent[$grid_settings['name']][$this->rowNumber-1][$this->columnNumber] != $row[$key])
					&&($this->rowNumber != 0))
					{
						$this->SetSubTotals($key, $this->gridContent[$grid_settings['name']][$this->rowNumber-1][$this->columnNumber], $fields, $grid_settings);
					}
			};

			//starts cell buffering
			ob_start();

			//data which goes before the main cell (e.g. plus/minus images)
			echo $before_cell;

			//if no formatting should be applied to the cell and its content
			if ($value['format'] === false)
			{
				$text = $row[$key];
				echo $text;
			}
			//if formatting should be done
			else
			{
				//checks if translation is needed and we deal with LANG_ constant
				if (($value['translate'])&&(ereg('^LANG_', $row[$key])))
				{
					eval("\$text = $row[$key];");
				}
				else
				{
					$text = $row[$key];
				}

				//applies user function ('func' feature)
				if (isset($value['func']))
				{
					$userFunction = str_replace('*', $row[$key], $value['func']);
					eval("\$text = $userFunction;");
				}

				//gets html attributes for the cell data. All '*' are replaced by $row[$key]
				if (isset($value['htmlAttributes']))
				{
					$value['htmlAttributes'] = str_replace('*', $row[$key], $value['htmlAttributes']);
					$value['htmlAttributes'] = str_replace('@', $row[$fields[$key]['id']], $value['htmlAttributes']);
				}

				//makes new line if necessary
				if (isset($value['split']))
				{
					$text = str_replace($value['split'], $value['split'].'<br />', $text);
				}

				//checks whether grid value should be an url
				if ((isset($value['url']))&&(!isset($value['data'])))
				{
					if ($value['url'] == '')
					{
						$this->link('', $text, $value['htmlAttributes']);
					}
					else
					{
						//checks whether to show 1 or more parameters in the url
						$url_ids = $this->GetParametersString($value[id], $row);

						//sets the url action
						if (!isset($value['action'])) $value['action'] = $object->config->defaultGridAction;

						//sets the alternative text
						if (isset($value['alt_text']))
						{
							if (is_array($value['alt_text']))
							{
								$alt_text = '';
								foreach ($value['alt_text'] as $alt_text_elem)
								{
									if (!empty($row[$alt_text_elem]))
										$alt_text .= ucfirst($fields[$alt_text_elem]['title']).': '.$row[$alt_text_elem].'<br />';
								}
							}
							else
							{
								if (!empty($row[$value[alt_text]]))
									$alt_text = ucfirst($fields[$value['alt_text']]['title']).': '.$row[$value[alt_text]];
							}
						}

						if ($current_controller->controller_model->db_structure[$key]['type'] == 'bit')
						{
							$text = $this->BooleanToText($row[$key]);
						}
						$this->link('/'.$value['url'].'/'.$value[action].$grid_settings['action'].'/'.$url_ids, $text, $value['htmlAttributes'], $alt_text);
					}
				}
				else
				{
					//checks if the field has 'bit' type and displays it properly
					if (($current_controller->controller_model->db_structure[$key]['type'] == 'bit')
						||((isset($grid_settings['controller']))&&($current_controller->GetModelObject($grid_settings['controller'])->db_structure[$key]['type'] == 'bit')))
					{
						$text = $this->BooleanToText($row[$key]);
						echo $text;
					}
					else if (($current_controller->controller_model->db_structure[$key]['type'] == 'date')
						||((isset($grid_settings['controller']))&&($current_controller->GetModelObject($grid_settings['controller'])->db_structure[$key]['type'] == 'date')))
					{
						$text = $this->DatetimeToDate($text);
						echo $text;
					}
					else
					{
						//checks if the field has 'percent' subtype and displays it properly
						if ($current_controller->controller_model->db_structure[$key]['subtype'] == 'percent')
						{
							$this->StatusBar($text);
						}
						else
						{
							echo $text;

							//if we have additional data in the field
							if (isset($value['data']))
							{
								//if we have data in array. Deprecated.
								if (is_array($value['data']))
								{
									foreach ($value['data'] as $data)
									{
										echo ' '.$row[$data].' ';
										$text .= ' '.$row[$data].' ';
									}
								}
								//if we have data in the string
								else
								{
									//gets values array
									$split_values_array = split("[^a-zA-Z_*]+", $value['data']);

									//gets separators array
									$split_separators_array = split("[a-zA-Z_*]+", $value['data']);

									//search for new line characters
									foreach ($split_separators_array as &$separator)
									{
										if ($separator == $value['split']) $separator = '<br />';
									}

									$i = 0;
									$cell_data = '';
									foreach ($split_values_array as $elem)
									{
										//transtates LANG_ constants
										if (strstr($elem, '*'))
										{
											$split_value_array = split('\*', $elem);
											eval("\$translation = {$split_value_array[1]};");
											$cell_data .= $translation.' ';
											$elem = $split_value_array[2];
										}
										else
										{
											//if the value is a language constant
											if ((ereg('^LANG_', $row[$elem]))) eval("\$row[\$elem] = $row[$elem];");
										}

										$cell_data .= (!empty($elem)) ? $row[$elem].$split_separators_array[$i+1] : $split_separators_array[$i+1];
										$i++;
									}

									//if it is an url
									if (isset($value['url']))
									{
										//checks whether to show 1 or more parameters in the url
										$url_ids = $this->GetParametersString($value['id'], $row);

										//sets the url action
										if (!isset($value['action'])) $value['action'] = $object->config->defaultGridAction;

										$this->link('/'.$value['url'].'/'.$value['action'].$grid_settings['action'].'/'.$url_ids, $cell_data, $value['htmlAttributes'], $alt_text);
									}
									else
									{
										echo $cell_data;
									}
									$text .= $cell_data;
								}
							}
						}
					}
				}
			}

			$this->gridTextContent[$grid_settings['name']][$this->rowNumber][$this->columnNumber] = strip_tags($text);

			//gets data for grid totals
			if ((isset($value['totals']))&&($grid_settings['use_totals']))
			{
				$this->gridTotals[$grid_settings['name']][$key]['sum'] += strip_tags($text);
				$this->gridTotals[$grid_settings['name']][$key]['count']++;
				$this->gridSubTotals[$grid_settings['name']][$key]['sum'] += strip_tags($text);
				$this->gridSubTotals[$grid_settings['name']][$key]['count']++;
			}

			//outs the cell content from buffer into array
			$this->gridContent[$grid_settings['name']][$this->rowNumber][$this->columnNumber] = ob_get_contents();

			//clears the buffer
			ob_end_clean();

			//displays cell content
			if ($this->isEditMode)
			{
				//if in edit mode
				$this->InputText($key, $this->gridTextContent[$grid_settings['name']][$this->rowNumber][$this->columnNumber], "class = '$this->inputTextCssClass' style = 'width: 100%;'");
			}
			else
			{
				echo $this->gridContent[$grid_settings['name']][$this->rowNumber][$this->columnNumber];
			}

			//$this->ContextMenu($data);

			echo "</td>";

			$this->columnNumber++;
			//Logger::Debug("cell for row #{$this->rowNumber} #$key ended");
		}

		/**
		 * Generates grid detailed row
		 *
		 * @param array $fields grid fields
		 * @param attay $row row
		 * @param array $grid_settings grid settings
		 */
		public function GridDetailedRow($fields, $row, $grid_settings)
		{
			global $current_controller;

			$column_width = 100/count($grid_settings[item_structure][0]);

			echo "<td>";

			echo "<table class = 'detailedGridItem' style = 'width: 100%'>";
				foreach ($grid_settings[item_structure] as $element_row)
				{
					echo "<tr>";
						foreach ($element_row as $key=>$value)
						{
							if (empty($value))
							{
								$cell_data = '';
							}
							else
							{
								switch ($current_controller->controller_model->db_structure[$value][type])
								{
									case 'text':
										$cell_data = "<span class = 'detailedGridItemLabel'>".ucfirst($fields[$value][title])."</span>:<br />";
										if (!empty($row[$value])) $cell_data .= "<div style = 'margin-top: 3px; border: 1px dotted #CCCCCC;'>$row[$value]</div>";
										break;
									default:
										$cell_data = "<span class = 'detailedGridItemLabel'>".ucfirst($fields[$value][title])."</span>: ".$row[$value];
										break;
								}

							}
							echo "<td style = 'width: $column_width%'>$cell_data</td>";
						}
					echo "</tr>";
				}
			echo "</table>";
			echo "</td>";

			//$this->gridContent[$grid_settings[name]][$this->rowNumber][0] = 'test';
		}

	    /**
		 * Generates a leading grid cell with the checkbox for the mass operations
		 *
  		 * @param array $row grid row
  		 * @param string $cellStyle cell style
  		 */
		public function CheckBoxCell($row, $cellStyle, $showCheckbox = 1, $grid_settings = '')
		{
			$id = (empty($grid_settings[id])) ? $row[id] : $row[$grid_settings[id]];
			echo "<td class='$cellStyle' width='15' style='text-align: center; padding: 0px 1px; text-indent: 0px;'>";
				if ($showCheckbox)
				{
					$this->checkBox('chk['.$id.']', 1, 0, 'id="chk" onclick="document.getElementById(\'massChk\').checked=false"');
				}
			echo '</td>';
		}

		/**
		 * Generates grid legend (with pictures)
		 *
		 * @param array $legend text and pictures to display
		*/
	    public function Legend($legend)
	    {
	    	echo "<table class='$this->legendTableCssClass'>";
			echo "<tr><td>";
			foreach ($legend[image_name] as $key => $value)
			{
				echo "<img src='".IMAGES.SEPARATOR.$value."' width='15' height='15'>&nbsp;".$legend[inscription][$key]."&nbsp;&nbsp;\n";
			}
			echo "</td></tr>";
			echo "</table>";
	    }

	    /**
	     * Displays grid's totals
	     *
	     * @param array $fields grid fields
	     * @param array $grid_settings grid settings
	     */
	    public function Totalize(array $fields, array $grid_settings)
	    {
	    	$totalsRow = '';

	    	$totalsRow .= "<tr class = '{$this->gridTotalsRowCssClass}'>";

			//displays a checkbox to the left of each row if necessary
			if ($grid_settings['use_mass_operations'])
			{
				$totalsRow .= "<td>&nbsp;</td>";
			}

	    	foreach ($fields as $key=>$value)
	    	{
				if ((isset($value['in_grid']))&&($value['in_grid'] == false)) continue;

				if (!empty($value['totals']))
				{
					$result = '';
					foreach ($value['totals'] as $total)
					{
						switch ($total)
						{
							case 'sum':
								$result .= "<span class = '$this->gridTotalsLabelCssClass'>".ucfirst(LANG_SUM).'</span>: '.$this->gridTotals[$grid_settings[name]][$key]['sum'].'<br />';
								break;
							case 'avg':
								$result .= "<span class = '$this->gridTotalsLabelCssClass'>".ucfirst(LANG_AVG).'</span>: '.round($this->gridTotals[$grid_settings[name]][$key]['sum']/$this->gridTotals[$grid_settings['name']][$key]['count'], 2).'<br />';
								break;
							case 'count':
								$result .= "<span class = '$this->gridTotalsLabelCssClass'>".ucfirst(LANG_COUNT).'</span>: '.$this->gridTotals[$grid_settings['name']][$key]['count'].'<br />';
								break;
						}
					}
				}
				else
				{
					$result = '';
				}

	    		$totalsRow .= "<td class = '{$this->gridTotalsCellCssClass}'>$result</td>";
	    	}
	    	$totalsRow .= "</tr>";

	    	$this->gridTotalsRow[$grid_settings['name']] = $totalsRow;
	    	echo $totalsRow;
	    }

	    /**
	     * Sets grid's subtotals
	     *
	     * @param string $group_field_name group field name
	     * @param string $group_field_value group field value
	     * @param array $fields grid fields
	     * @param array $grid_settings grid settings
	     */
	    public function SetSubTotals($group_field_name, $group_field_value, array $fields, array $grid_settings)
	    {
	    	foreach ($fields as $key=>$value)
	    	{
				if (!empty($value['totals']))
				{
					foreach ($value['totals'] as $total)
					{
						switch ($total)
						{
							case 'sum':
								$this->subTotals[$grid_settings['name']][$group_field_name][$group_field_value][$key]['sum'] =
									$this->gridSubTotals[$grid_settings['name']][$key]['sum'];
								$this->gridSubTotals[$grid_settings['name']][$key]['sum'] = 0;
								break;
//							case 'avg':
//								$result .= "<span class = '$this->gridTotalsLabelCssClass'>".ucfirst(LANG_AVG).'</span>: '.round($this->gridTotals[$grid_settings[name]][$key][sum]/$this->gridTotals[$grid_settings[name]][$key][count], 2).'<br />';
//								break;
							case 'count':
								$this->subTotals[$grid_settings['name']][$group_field_name][$group_field_value][$key]['count'] =
									$this->gridSubTotals[$grid_settings['name']][$key]['count'];
								$this->gridSubTotals[$grid_settings['name']][$key]['count'] = 0;
								break;
						}
					}
				}
				else
				{
					$result = '';
				}
	    	}
	    }

	    /**
	     * Displays grid's subtotals
	     *
	     * @param array $fields grid fields
	     * @param array $grid_settings grid settings
	     */
	    public function SubTotalize(array $fields, array $grid_settings)
	    {
	    	//Debugger::VarDump($this->subTotals);

	    	$subtotalsTable = '';

	    	foreach ($this->subTotals[$grid_settings['name']] as $groupByFieldName=>$values)
	    	{
	    		$subtotalsTable .= "<div style = 'margin-top: 10px; '>".LANG_SUBTOTALS."</div>";
	    		$subtotalsTable .= "<table class = '$this->gridCssClass' style = 'width: auto; '>";
	    			$i = 0;
	    			foreach ($values as $groupByFieldValue => $vals)
	    			{
	    				//shows title only once
	    				if ($i == 0)
	    				{
		    				$subtotalsTable .= "<tr class = '$this->gridHeaderRowCssClass'>";
		    					$subtotalsTable .= "<td class = '$this->gridHeaderCellCssClass'>".ucfirst($fields[$groupByFieldName]['title'])."</td>";
		    				    foreach ($vals as $subtotalFieldName => $vs)
		    					{
		    						$subtotalsTable .= "<td class = '$this->gridHeaderCellCssClass'>".ucfirst($fields[$subtotalFieldName]['title'])."</td>";
		    					}
		    				$subtotalsTable .= "</tr>";
	    				}

	    				//shows subtotals content
	    				$subtotalsTable .= "<tr class = '$this->gridRowEvenCssClass'>";
	    					$subtotalsTable .= "<td class = '$this->gridCellCssClass'>$groupByFieldValue</td>";
	    					foreach ($vals as $subtotalFieldName => $vs)
	    					{
	    						$subtotalsTable .= "<td class = '$this->gridCellCssClass'>";
	    						$j = 0;
	    						foreach ($vs as $operation => $value)
	    						{
	    							if ($j != 0) break;
	    							$subtotalsTable .= $operation.': '.$value;
	    							$j++;
	    						}
	    						$subtotalsTable .= "</td>";
	    					}
	    				$subtotalsTable .= "</tr>";
	    				$i++;
	    			}
	    		$subtotalsTable .= "</table>";

				$this->gridSubTotalsTable[$grid_settings['name']] = $subtotalsTable;

	    		//displays subtotals
	    		echo $subtotalsTable;
	    	}
	    }

		/**
		 * Generates live (JS) filter row
		 *
		 * @param array $labels grid columns
  		 * @param boolean $has_first_column indicates whether to display filter box or not (default: 0)
		 * @param boolean $has_actions indicates whether to display the 1st column (default: 1)
		 * @param boolean $has_mass_operations = 0 indicates whether the grid has mass operations (default: 0)
		 * @param boolean $use_sort indicates whether the grid uses sorting (default: 1)
		 */
		public function GridLiveFilter(array $labels, $has_first_column = 0, $has_actions = 1, $grid_settings)
		{
			if (!isset($grid_settings[use_mass_operations])) $grid_settings[use_mass_operations] = 0;
			
			echo "<tr class='$this->gridHeaderRowCssClass' id = '$grid_settings[name]_live_filter'>";
        	
			if ($has_first_column) echo "<td> </td>";
        	if ($grid_settings[use_mass_operations])
        	{
        		echo "<td class='$this->gridHeaderCellCssClass' style = 'text-align: center'>";
					echo "<img src='".IMAGES.$this->deleteImageName."' class='$this->actionButtonCssClass' onClick = 'ClearLiveFilter(this, \"{$grid_settings[name]}\");'>";
        		echo "</td>";
        	}
			
        	//first filter cell
        	$this->JavaScript('firstFilterCell = null; ');
        	
        	//displays live filter input fields
        	$i = 1;
        	foreach ($labels as $k=>$v)
        	{
				if ((isset($v['in_grid']))&&($v['in_grid']==false)) continue;
				
				echo '<td style = "padding: 1px 3px;" class="'.$this->gridHeaderCellCssClass.'">';
        			if ((!isset($v['js_filter']))||($v['js_filter'] != false))
					{
						$this->InputText("$k", "", "onKeyUp = 'LiveFilter(this, \"{$grid_settings[name]}\");' autocomplete='off' onfocus = 'this.select(); ' class = '$this->inputTextLiveFilterCssClass' id = '$k'");

						$this->JavaScript('if (firstFilterCell == null) firstFilterCell = "'.$k.'"; shortcut.add("F'.$i.'", function() {document.getElementById("'.$k.'").focus(); document.getElementById("'.$k.'").select(); UnmarkGridRow(); /*MarkGridRow(\'0\', \'\', \''.$grid_settings[name].'\');*/}, {"disable_in_input":false})');
						$i++;

					}				
				echo '</td>';
        	}
        	
        	//cart header if needed
        	if ($grid_settings[use_cart]) 
        	{
        		echo "<td class='$this->gridHeaderCellCssClass' style = 'text-align: center'>&nbsp;";
        		echo "</td>";
        	}
        	
        	//displays clear live filter button
        	if ($has_actions) 
        	{
        		echo "<td class='$this->gridHeaderCellCssClass' style = 'text-align: center'>";
        		echo "</td>";
        	}
        	
        	echo '</tr>';
		}

	    /**
		 * Generates grid's header with the sorting arrows
		 *
		 * @param array $labels columns labels
  		 * @param boolean $has_first_column indicates whether to display filter box or not (default: 0)
		 * @param boolean $has_actions indicates whether to display the 1st column (default: 1)
		 * @param boolean $has_mass_operations = 0 indicates whether the grid has mass operations (default: 0)
		 * @param boolean $use_sort indicates whether the grid uses sorting (default: 1)
		*/
		public function TableHeaderRowMultiCellsSort(array $labels, $has_first_column = 0, $has_actions = 1, $has_mass_operations = 0, $use_sort=1, $grid_settings = array())
		{
			//defines the grid name
			if (!isset($grid_settings[name])) $grid_settings[name] = 'grid';

			//counts width for each <td>
			$count_labels = 0;
			foreach ($labels as $label)
			{
				if ((($label[in_grid] == true)||(!isset($label[in_grid])))&&(!isset($label[width]))) $count_labels++;
			}
			$cell_width = 100/$count_labels;
			$cell_width = $cell_width.'%';

			//displays headers for the first column and mass operations if needed
			echo "<tr class='$this->gridHeaderRowCssClass'>";
        	if ($has_first_column) echo "<td width='15'> </td>";

        	if ($this->isEditMode)
        	{
        		//edit mode code here...
        	}
        	else if ($has_mass_operations)
        	{
        		echo "<td class='$this->gridHeaderCellCssClass' style = 'width: 1%; text-align: center'>";
        		$this->CheckBox('massChk', 1, 0, 'id="massChk" onclick = "CheckAllBoxes(\'chk\',this.checked)"');
        		echo "</td>";
        	}

        	//removes unnecessary get parameters
			$this->RemoveEscapedGetParameters();

			$i = 0;
        	foreach ($labels as $k=>$v)
        	{
				if ((isset($v[in_grid]))&&($v[in_grid] == false)) continue;

				if (!empty($v[width])) $td_width = $v[width];
				else $td_width = $cell_width;

				echo '<td style = "width: '.$td_width.'" class="'.$this->gridHeaderCellCssClass.'"><table border = "0" cellpadding = "0" cellspacing = "0" width="100%"><tr class="'.$this->gridHeaderRowCssClass.'"><td style = "border: 0px; padding: .6em 10px;">'.ucfirst($v[title]).' <nobr></td><td class ="'.$this->gridSortCellCssClass.'">';
				if (!is_numeric($k)&&($use_sort==1)&&(($v[sort]!=false)||(!isset($v[sort]))))
				{
		  			//adds '?' to the link if necessary
					if (!strstr($_SERVER['REQUEST_URI'], '?')) $_SERVER['REQUEST_URI'] = $_SERVER['REQUEST_URI'].'?';

					//eleminates multiple 'sort_by' and 'sort_method' from url
					$_SERVER['REQUEST_URI'] = ereg_replace("(&){0,1}(sort_by=){1}([a-z_])*", '', $_SERVER['REQUEST_URI']);
					$_SERVER['REQUEST_URI'] = ereg_replace("(&){0,1}(sort_method=){1}([a-z_])*", '', $_SERVER['REQUEST_URI']);

					HtmlHelper::link($_SERVER['REQUEST_URI'].'&sort_by='.$k.'&sort_method=asc','<img src="'.IMAGES.SEPARATOR.$this->arrowUpImageName.'" />');
        			HtmlHelper::link($_SERVER['REQUEST_URI'].'&sort_by='.$k.'&sort_method=desc','<img src="'.IMAGES.SEPARATOR.$this->arrowDownImageName.'" />');
				}
        		echo '</nobr></td></tr></table></td>';

        		$this->gridHeader[$grid_settings[name]][$i] = strip_tags($v[title]);
        		$i++;
        	}
        	if ($has_actions)
        		echo '<td class="'.$this->gridHeaderRowCssClass.'"> <table cellpadding = "1" cellspacing = "0" width="100%"><tr class="'.$this->gridHeaderRowCssClass.'"><td>'.ucfirst(LANG_ACTIONS).'</td></tr></table></td>';
        	echo '</tr>';
   		}

	    /**
		 * Generates pagination table
		 *
		 * @param integer $num_results total number of results
  		 * @param integer $results_per_page indicates how many records to display on one page
  		 * @param integer $page page number
		*/
   		public function Pagination($num_results, $results_per_page, $page)
		{
		  //if there is nothing to display
		  if (empty($num_results)) return;

		  global $current_controller;
		  $link_class = " class='$this->paginationLinkCssClass' ";

		  $this->RemoveEscapedGetParameters();

		  //eleminates multiple 'page' from url
		  $_SERVER['REQUEST_URI'] = ereg_replace("(&){0,1}(page=){1}([0-9,])*", '', $_SERVER['REQUEST_URI']);

		  //adds '?' to the url if needed
		  if (!strstr($_SERVER['REQUEST_URI'], '?')) $_SERVER['REQUEST_URI'] = $_SERVER['REQUEST_URI'].'?';

		  //sets $PHP_SELF if it is not set
		  if (!isset($PHP_SELF)) $PHP_SELF = $_SERVER['REQUEST_URI'];

		  //RESULTS PER PAGE SELECT
		  $results_per_page_select = "<form id = 'results_per_page_form' action = '' method = 'get'><td class='$this->paginationCellCssClass' style='vertical-align: bottom'>";

		  //adds hidden fields with the previous get values
		  foreach ($current_controller->request->get as $key=>$value)
		  {
		  	if (($key == 'submit') || ($key == 'page_number')) continue;

		  	$results_per_page_select .= "<input id = '".$key."' name='".$key."' type='hidden' value='".$value."'/>";
		  }

		  //generates select tag
    	  $results_per_page_select .= "<select onChange = 'document.getElementById(\"page\").disabled = \"disabled\"; document.getElementById(\"results_per_page_form\").submit()' style='width: 50px; height: 18px;' class='$this->comboboxCssClass' name = 'page_number'>";

		  //to display all records
    	  if ($num_results == $current_controller->request->get[page_number]) $selected = "selected = 'selected'";
		  else $selected = '';
    	  $results_per_page_select .= "<option value='$num_results' $selected>".LANG_SHOW_ALL."</option>";

    	  //generates options
    	  for ($i=1; $i<=20; $i++)
		  {
		  	$j = $i*10;
		  	if ($j == $current_controller->request->get[page_number]) $selected = "selected = 'selected'";
		  	else $selected = '';
		  	$results_per_page_select .= "<option value='$j' $selected>$j</option>";
		  }
		  $results_per_page_select .= "</select></td></form>";

		  //if all records do not fit in one page
		  if($num_results/$results_per_page > 1)
		  {
			if(empty($page)) $page = 1;
			echo "
	        	<table border='0' cellpadding='2' cellspacing='0' align='right' class='$this->paginationTableCssClass'>
		          <tr>
		            <td>&nbsp;</td>
		            <td class='$this->paginationCellCssClass'>".LANG_RESULTS.": ".$num_results."</td>
		            <td class='$this->paginationCellCssClass'></td>
		            <td class='$this->paginationCellCssClass'><strong>
			";

			//previous page
		    if($page != 1)
		    {
		        $page_prev = $page-1;
		        $this->link("$PHP_SELF&page=$page_prev", '<  ', $link_class);
		    }
		    else
		    {
		        echo '<  ';
		    }

			$num_pages = floor($num_results/$results_per_page);

			$pages_begin = 2;
			$pages_end = 2;
			$pages_around = 1;
			$pages_sides = 4;

			//TODO: To fix the bug (when 6 pages par example)
			if ($num_pages>$pages_sides)
			{
				if (($page>=$pages_sides)&&($page<=($num_pages-$pages_sides)))
				{
					for($i = 1; $i <= $pages_begin; $i++) $this->link("$PHP_SELF&page=$i", $i.' ', $link_class);
					if (($page-$i)!=1) echo " ... ";

					for($i = $page-$pages_around; $i <= $page+$pages_around; $i++)
				    {
				    	if ($i==0) continue;
				        if($i == $page)
				        {
				            echo($i." ");
				        }
				        else
				        {
				            $this->link("$PHP_SELF&page=$i", $i.' ', $link_class);
				        }
				    }

					echo " ... ";
					for($i = $num_pages - ($pages_end - 1); $i <= $num_pages; $i++) $this->link("$PHP_SELF&page=$i", $i.' ', $link_class);
				}
				else if ($page<$pages_sides)
				{
					for ($i = 1; $i <= $pages_sides; $i++)
				    {
				        if($i == $page)
				        {
				            echo($i." ");
				        }
				        else
				        {
				            $this->link("$PHP_SELF&page=$i", $i.' ', $link_class);
				        }
				    }
					echo " ... ";
					for ($i = ceil($num_results/$results_per_page)-($pages_end-1); $i <= $num_pages; $i++) $this->link("$PHP_SELF&page=$i", $i.' ', $link_class);
				}

				else
				{
					for ($i = 1; $i <= $pages_begin; $i++) $this->link("$PHP_SELF&page=$i", $i.' ', $link_class);
					if (($page-$i)!=1) echo " ... ";

					//sets the page number to start from
					if ($i > $num_pages - $pages_sides) $start = $i;
					else $start = $num_pages - $pages_sides;

					for ($i = $start; $i <= $num_pages; $i++)
					{
				        if($i == $page)
				        {
				            echo($i." ");
				        }
				        else
				        {
				            $this->link("$PHP_SELF&page=$i", $i.' ', $link_class);
				        }
				    }
				}
			}

			else
			{
				//pages with a comlete set of results
				for($i = 1; $i <= $num_pages; $i++)
			    {
			        if($i == $page)
			        {
			            echo($i." ");
			        }
			        else
			        {
			            $this->link("$PHP_SELF&page=$i", $i.' ', $link_class);
			        }
			    }
			}

		    //if we have additional results
		    if(($num_results % $results_per_page) != 0){
		        if($i == $page){
		            echo($i." ");
		        }else{
		            $this->link("$PHP_SELF&page=$i", $i.' ', $link_class);
		        }
		    }

		    //next page
			if(($num_results - ($results_per_page * $page)) > 0){
			    $page_next = $page+1;

		        $this->link("$PHP_SELF&page=$page_next", '  >', $link_class);
			}else{
			    echo '  >';
			}
			echo "
		            </strong>
		            </td>$results_per_page_select
		          </tr>
	      		</table>
			";
		  }
		  else echo "
	        	<table border='0' cellpadding='2' cellspacing='0'>
		          <tr>
		            <td>&nbsp</td>
		            <td><strong>".LANG_RESULTS.": ".$num_results."</strong></td>$results_per_page_select
		          </tr>
		        </table>";

		}


		# GRID FUNCTIONS #

		/**
		* Generates print link
		*
		* @param string $grid_name grid name
		*/
		public function GeneratePrintLink($grid_name, $grid_settings)
		{
    		global $current_controller;
    
    		//puts grid content into session
    		$_SESSION[grids][$grid_name][content] = $this->gridTextContent[$grid_name];
    		$_SESSION[grids][$grid_name][header] = $this->gridHeader[$grid_name];
    		$_SESSION[grids][$grid_name][title] = (empty($grid_settings[title])) ? $current_controller->page_title : $grid_settings[title];
    		$_SESSION[grids][$grid_name][subtotals] = $this->gridSubTotalsTable[$grid_name];
    		$_SESSION[grids][$grid_name][totals] = $this->gridTotalsRow[$grid_name];
    
    		$this->JavaScript("if (document.getElementById('{$grid_name}_print')) {link = document.getElementById('{$grid_name}_print'); link.href = '/printpreview/grid/?name=$grid_name'; }");
        }

		/**
		* Generates export to excel link
		*
		* @param string $grid_name grid name
		* @param array $grid_settings grid settings
		*/
		public function GenerateExportToExcelLink($grid_name, $grid_settings)
		{
            global $current_controller;
            
            //puts grid content into session
            $_SESSION[grids][$grid_name]['content'] = $this->gridTextContent[$grid_name];
            $_SESSION[grids][$grid_name]['header'] = $this->gridHeader[$grid_name];
            $_SESSION[grids][$grid_name]['title'] = (empty($grid_settings['title'])) ? $current_controller->page_title : $grid_settings['title'];
            
            $this->JavaScript("if (document.getElementById('{$grid_name}_export_excel')) {link = document.getElementById('{$grid_name}_export_excel'); link.href = '/printpreview/gridExportToExcel/?name=$grid_name'; }");
		}

	    /**
		 * Returns the ids' string
		 *
		 * @param array $ids ids
  		 * @param array $row grid row
  		 * @return string $url_ids string with the ids
		*/
		public function GetParametersString ($ids, $row)
		{
			if (is_array($ids))
			{
				$url_ids = '';
				foreach ($ids as $id)
				{
					$url_ids = $url_ids.$row[$id].'/';
				}
			}
			else
			{
				$url_ids = $row[$ids];
			}
			return $url_ids;
		}

	    /**
		 * Returns the ids' string for the actions
		 *
		 * @param array $grid_settings grid settings
  		 * @param array $row grid row
  		 * @return string $url_ids string with the ids
		*/
		public function GetActionParametersString ($grid_settings, $row)
		{
			if (isset($grid_settings[id]))
			{
				if (is_array($grid_settings[id]))
				{
					$action_ids = '';
					foreach ($grid_settings[id] as $id)
					{
						$action_ids = $action_ids.$row[$id].'/';
					}
				}
				else
					$action_ids = $row[$grid_settings[id]];
			}
			else $action_ids = $row['id'];

			return $action_ids;
		}

		/**
		 * Gets row attributes
		 *
		 * @param array $row grid row
		 * @param string $conditional_row_attributes conditional row attributes
		 * @return string
		 */
		public function GetRowAttributes($row, $conditional_row_attributes)
		{
			if (!empty($conditional_row_attributes))
			{
				$conditionalRowAttributesArray = explode('|', $conditional_row_attributes);
				$rowConditions = explode(',', $conditionalRowAttributesArray[1]);
				eval ("if (\$row[\$rowConditions[0]] $rowConditions[1] \$rowConditions[2]) {\$additionalRowAttributes = \$conditionalRowAttributesArray[0];}");
				return $additionalRowAttributes;
			}
			else
			{
				return '';
			}
		}

		/**
		 * Return the array with the disabled grid operations
		 *
		 * @param array $row grid row
		 * @param string $conditional_disable_operation conditions to be parsed
		 * @return disabled operations' array
		 */
		public function GetDisabledOperations(array $row, $conditional_disable_operation)
		{
			if (!empty($conditional_disable_operation))
			{
				$conditionalDisableOperationsArray = explode('|', $conditional_disable_operation);
				$rowConditions = explode(',', $conditionalDisableOperationsArray[1]);
				eval ("if (\$row[\$rowConditions[0]] $rowConditions[1] \$rowConditions[2]) {\$disabledOperations[\$conditionalDisableOperationsArray[0]] = true;}");

				return $disabledOperations;
			}
			else
			{
				return '';
			}
		}

		/**
		 * Converts boolean to text (0/1 to no/yes)
		 *
		 * @param boolean|string|integer $value value to be converted
		 * @return string converted string
		 */
		public function BooleanToText($value = null)
		{
			if ($value == null) return '';
			return $value ? LANG_YES : LANG_NO;
		}

		/**
		 * Converts datetime to date
		 *
		 * @param string $value value to be converted
		 * @return string converted to date string
		 */
		public function DatetimeToDate($value)
		{
			return substr($value, 0, 10);
		}
		
		/**
		 * Marks the selected row. If it is not specified - marks the first row
		 * @param array $grid_settings grid settings
		 */
		public function MarkGridRow($grid_settings)
		{
			global $current_controller;

			if (!isset($current_controller->request->get['selectedrow']))
			{
				$this->JavaScript("MarkGridRow('0', '', '{$grid_settings[name]}');");
			}
			else
			{
				$this->JavaScript("MarkGridRow('{$current_controller->request->get[selectedrow]}', '', '{$grid_settings[name]}');");
			}
		}

		/**
		 * Generates shortcuts for the grid
		*/
		public function Shortcuts($grid_settings)
		{
			global $object;

			//disables row selection if needed
			if (!$grid_settings['disableRowSelection'])
			{
    			$this->JavaScript('shortcut.add("Enter", function() {SelectGridRowByKeyPress("'.$object->config->defaultGridAction.'", "'.$grid_settings[name].'");})');
    			$this->JavaScript('shortcut.add("Alt+v", function() {SelectGridRowByKeyPress("view", "'.$grid_settings[name].'");})');
    			$this->JavaScript('shortcut.add("Alt+e", function() {SelectGridRowByKeyPress("edit", "'.$grid_settings[name].'");})');
    			$this->JavaScript('shortcut.add("Alt+d", function() {SelectGridRowByKeyPress("delete", "'.$grid_settings[name].'");})');
    			$this->JavaScript('shortcut.add("Alt+p", function() {SelectGridRowByKeyPress("permissions", "'.$grid_settings[name].'");})');
    			$this->JavaScript('shortcut.add("Alt+c", function() {SelectGridRowByKeyPress("add_child", "'.$grid_settings[name].'");})');
			}

			$this->JavaScript('shortcut.add("left", function() {SelectGridRowByKeyPress("collapse", "'.$grid_settings[name].'");})');
			$this->JavaScript('shortcut.add("right", function() {SelectGridRowByKeyPress("expand", "'.$grid_settings[name].'");})');
			$this->JavaScript('shortcut.add("up", function() {ShiftGridRowByKeyPress("up", "'.$grid_settings[name].'");}, {"disable_in_input":false})');
			$this->JavaScript('shortcut.add("down", function() {/*document.getElementById("alpha_num_filter_a").focus();*/ ShiftGridRowByKeyPress("down", "'.$grid_settings[name].'");}, {"disable_in_input":false})');
			$this->JavaScript('shortcut.add("esc", function() {window.location.replace("'.$_SERVER[REDIRECT_URL].'");})');
		}

		/**
		 * Removes escaped get parameters ($this->escapeGetParameters) from the GET-string
		 *
		 */
		private function RemoveEscapedGetParameters()
		{
		    foreach ($this->escapeGetParameters as $param)
        	{
        		$_SERVER['REQUEST_URI'] = ereg_replace("(&){0,1}($param=){1}([a-z0-9_])*", '', $_SERVER['REQUEST_URI']);
        	}
		}

		/**
		 * Count elements level in the heirarchical list
		 *
		 * @param array $rows all rows in the array
  	 	 * @param integer $parent_id parent element id
		*/
		public function CountLevel($rows, $parent_id)
		{
			foreach ($rows as $row)
			{
				if ($row[id] == $parent_id)
				{
					$this->count++;
					$this->CountLevel($rows, $row[parent_id]);
				}
			}
		}		
		
		//TODO: to make and implement
		public function ContextMenu($data)
		{
			global $current_controller;

		}


		# FILTERS #

	    /**
		 * Generates simple filter box (table)
		 *
		 * @param array $fields fields, used in filtering (associative array)
  		 * @param string $show_all_url the url that points to the "show all" script (default: $current_controller->name)
		*/
		public function Filter($fields, $show_all_url = '', $grid_settings)
		{
			Logger::Debug("Filter started");
			global $current_controller;

			if ($_SESSION[$current_controller->name][show_filter]==0)
			{
				$table_style=' display:none; ';
				$image = $this->plusImageName;
			}
			else
			{
				$table_style=' ';
				$image = $this->minusImageName;
			}

			if (empty($show_all_url)) $show_all_url = '/'.$current_controller->name;
			echo "<table class = '$this->filterTableCssClass' style = 'border-bottom-width: 0px;' align = 'center' width = '100%' cellpadding = '1' cellspacing = '0'>";
			$this->form('', 'GET', 'name = "filter" id = "filter"');
			echo "<tr><td colspan = '2' class = '$this->filterHeaderCellCssClass'><img id = 'show_hide_img_{$grid_settings[name]}' src='".IMAGES.$image."' onClick = 'ShowHideBox(\"filter_box_{$grid_settings[name]}\", \"".$current_controller->name."\", \"ShowHideFilter\", \"show_hide_img_{$grid_settings[name]}\", \"".IMAGES.$this->plusImageName."\", \"".IMAGES.$this->minusImageName."\");' /> ".LANG_SEARCH."</td>";
			echo "<td style = 'text-align: right' class = '$this->filterHeaderCellCssClass'>&nbsp;";

			//clears filter cache if necessary
			if ($current_controller->request->get[filterCached] === '0') $_SESSION[filter][$_SERVER[REDIRECT_URL]] = array();

			//generates link for clearing the filter cache if the filter data is cached
			if (!empty($_SESSION[filter][$_SERVER[REDIRECT_URL]]))
			{
				//adds '?' to the url if needed
			    if (!strstr($_SERVER['REQUEST_URI'], '?')) $_SERVER['REQUEST_URI'] = $_SERVER['REQUEST_URI'].'?';

			    $this->Link($_SERVER[REQUEST_URI].'&filterCached=0', ' ['.LANG_CLEAR_FILTER_CACHE.'] ', "style = 'font-weight: 400'");
			}

			echo "</td></tr></table><div id = 'filter_box_{$grid_settings[name]}' style='$table_style'>";

			//tab menu
			if (!empty($grid_settings[filters]))
			{
				echo "<table id='filter' align='center' width = '100%' cellpadding ='0' cellspacing = '0' style='margin: 0px; padding: 0px;'>";
				echo '
					<script type="text/javascript">
						//SYNTAX: ddtabmenu.definemenu("tab_menu_id", integer OR "auto")
						ddtabmenu.definemenu("ddtabs1", -1) //initialize Tab Menu #1 with 1st tab selected
					</script>

					<tr><td colspan="2">
					<div id="ddtabs1" class="basictab">
						<ul>';

					foreach ($grid_settings[filters] as $key=>$value)
					{
						echo '<li><a rel="'.$key.'">'.$value.'</a></li>';
					}
					echo '	</ul>
					</div>
					</td></tr>';

					echo '<div class="tabcontainer">';

						foreach ($grid_settings['filters'] as $k=>$v)
						{
							echo '</table><div id="'.$k.'" class="tabcontent">';
							echo "<table id='filter' class='filter' align='center' width = '100%' cellpadding ='1' cellspacing = '0' style='margin-top: 0px; border-top-width: 0px;'>";

							//filter rows
							foreach ($fields as $key=>$value)
							{
								if (($value['filter'])||($value['fast_search_filter']))
								{
									if (!isset($value[filter_name])) $value[filter_name] = 'general';
									if ($value[filter_name]==$k)
									{
										Logger::Debug("Filter row ($key) started");
										$this->FilterRow($key, $value);
										Logger::Debug("Filter row ($key) ended");
									}
								}
							}
							echo '</table></div>';
							echo "<table id='filter' class='$this->filterTableCssClass' align='center' width = '100%' cellpadding ='1' cellspacing = '0' style='margin-top: 0px; border-top-width: 0px;'>";

						}

					echo '</div>';
			}

			else
			{
				echo "<table id='filter' class='$this->filterTableCssClass' align='center' width = '100%' cellpadding ='1' cellspacing = '0' style='margin-top: 0px; border-top-width: 0px;'>";

				//filter rows
				foreach ($fields as $key=>$value)
				{
					if (($value['filter'])||($value['fast_search_filter']))
					{
						Logger::Debug("Filter row ($key) started");
						$this->FilterRow($key, $value);
						Logger::Debug("Filter row ($key) ended");
					}
				}
			}

			echo "<tr id='filter' style='$tr_style'><td></td><td align='right'>";
			$this->submit('submit', LANG_SEARCH, "class='$this->submitButtonCssClass'");
			echo " ";

		    $this->cancel('/'.$current_controller->name.'/', ucfirst(LANG_SHOW_ALL), "class='$this->submitButtonCssClass'");
			echo "</td></tr>";
			echo "</form></table></div>";

			Logger::Debug("Filter ended");
		}

	    /**
		 * Generates filter row
		 *
		 * @param array $fields fields, used in filtering (associative array)
		*/
		public function FilterRow($key, $value)
		{
			global $current_controller;

			echo "<tr id='filter'>";
				echo "<td class='$this->filterLabelCellCssClass' style='width: 300px;'>".ucfirst($value[title])."</td>";
				echo "<td>";
				switch ($current_controller->controller_model->db_structure[$key][type])
				{
					case 'bit':
						echo $this->combo($key, array('0'=>array('id'=>'', 'name'=>'-'),'1'=>array('id'=>'1', 'name'=>LANG_YES), '2'=>array('id'=>'0', 'name'=>LANG_NO)), $current_controller->request->get[$key], "class='$this->comboboxCssClass' style='width: 50px'");
						break;
					case 'datetime':
						echo $this->inputText($key, $current_controller->request->get[$key], 'readonly = "readonly" class="'.$this->inputTextCssClass.'" style="width: 100px" onfocus="popUpCalendar(this, filter.'.$key.', \'yyyy/mm/dd\')"');
						echo "<img src = '".IMAGES."/btnDelete.gif' onclick = 'filter.$key.value = \"\"' style = 'margin-left: 2px; vertical-align: middle; cursor: pointer' />";
						break;
					default:
						if (isset($value[model]))
						{
							if (isset($_SESSION[filter][$_SERVER[REDIRECT_URL]][$value[id]]))
							{
								$list = $_SESSION[filter][$_SERVER[REDIRECT_URL]][$value[id]];
								$this->filterCached = true;
							}
							else if (!isset($value[proc]))
							{
								$list = $current_controller->GetModelObject($value[model])->ListItems();
							}
							else
							{
								$list = $current_controller->GetModelObject($value[model])->ListItems('', $value[proc]);
							}

							//checks if we deal with the LANG_ constant
							if (($value[translate])&&(!$this->filterCached))
							{
								foreach ($list as &$elem)
								{
									foreach ($elem as &$el)
									{
										if (strstr($el, 'LANG_'))
										{
											eval("\$el = $el;");
										}
									}
								}
							}

							switch ($value[filter_type])
							{
								case 'CheckBoxGroup':
									echo "<nobr>";
									$this->FilterCheckBoxGroup($key, $list);
									echo "</nobr>";
									break;
								default:
									//for multi-level lists
									if ((isset($list[0][parent_id]))&&(!$this->filterCached)) $list = $this->GetMultiLevelList($list);

									//selectbox with fast search (optional)
									echo "<nobr>";
									$this->select($value[id], $list, $current_controller->request->get[$value[id]], "class='$this->comboboxCssClass' id='".$value[id]."'");
									echo "&nbsp;&nbsp;&nbsp;";
									if ($value[fast_search_filter])
										$this->inputText('search_field', ' '.LANG_FAST_SEARCH.'...', "class='$this->inputTextSearchCssClass' id = 'search_field_".$value[id]."' style='width:120px' onClick='this.value=\"\"' onKeyUp='FastSearch(\"".$value[id]."\")'");
									echo "</nobr>";
							}

							//caches the list into SESSION
							$_SESSION[filter][$_SERVER[REDIRECT_URL]][$value[id]] = $list;
						}
						else
						{
							if ((isset($value[filter_array])) || (isset($value[filter_service])))
							{
								//if we filter by array
								if (isset($value[filter_array]))
								{
									//for multi-level lists
									if (isset($value[filter_array][0][parent_id])) $value[filter_array] = $this->GetMultiLevelList($list);

									//translates th array if needed
									$value[filter_array] = $this->TranslateArray($value[filter_array], true);
								}
								//if data is taken from a web service
								else if (isset($value[filter_service]))
								{
									//takes data from session if cached
									if (isset($_SESSION[filter][$_SERVER[REDIRECT_URL]][$value[id]]))
									{
										$value[filter_array] = $_SESSION[filter][$_SERVER[REDIRECT_URL]][$value[id]];
										$this->filterCached = true;
									}
									else
									{
										$webService = new WebServices();
										$tempFilter = $webService->Call($value[filter_service][wsdl_path], $value[filter_service][function_name], '', $value[filter_service][options]);
										eval("\$value[filter_array] = \$tempFilter{$value[filter_service][response_path]}; ");
									}
								}

								//selectbox with fast search (optional)
								echo "<nobr>";
								$this->select($value[id], $value[filter_array], $current_controller->request->get[$value[id]], "class='$this->comboboxCssClass' id='".$value[id]."'");
								echo "&nbsp;&nbsp;&nbsp;";
								if ($value[fast_search_filter])
									$this->inputText('search_field', ' '.LANG_FAST_SEARCH.'...', "class='$this->inputTextSearchCssClass' id = 'search_field_".$value[id]."' style='width:120px' onClick='this.value=\"\"' onKeyUp='FastSearch(\"".$value[id]."\")'");
								echo "</nobr>";

								//caches the list into SESSION
								$_SESSION[filter][$_SERVER[REDIRECT_URL]][$value[id]] = $value[filter_array];
							}
							else
							{
								if ((isset($current_controller->controller_model->db_structure[$key][by_default]))&&(!isset($current_controller->request->get[$key])))
								{
									$current_controller->request->get[$key] = $current_controller->controller_model->db_structure[$key][by_default];
								}
								$this->InputText($key, $current_controller->request->get[$key], "class='$this->inputTextCssClass'");
							}
						}
				}
				echo "</td>";
			echo "</tr>";
		}

	    /**
		 * Generates check box group for the filter
		 *
		 * @param string $field_name field name
  		 * @param array $value_array associative array with the data for the checkbox group
		*/
		public function FilterCheckBoxGroup($field_name, $value_array)
		{
			global $current_controller;

			foreach ($value_array as $element)
			{
				$value = array_shift($element);
				$text = array_shift($element);
				$this->checkBox($field_name.'_values['.$value.']', $value, $current_controller->request->get[$field_name.'_values'][$value], "class = '$this->checkboxCssClass' onClick = 'AddRemoveValues(this.value, \"$field_name\")'");
				echo "<span class = 'label'>".$text."</span><br />";
			}
			$this->hidden($field_name, '');
		}

		/**
		 * Generates alphabetic/numeric filter
		 *
		 * @param array $elements filter elements
		 */
		public function AlphabeticFilter(array $filters)
		{
			global $current_controller;

		  	//eleminates multiple get parameters from url
		  	$_SERVER['REQUEST_URI'] = ereg_replace("(&){0,1}($filters[field]=){1}([a-z0-9\%E0-\%FF])*", '', $_SERVER['REQUEST_URI']);

		  	//adds '?' to the url if needed
			if (!strstr($_SERVER['REQUEST_URI'], '?')) $_SERVER['REQUEST_URI'] = $_SERVER['REQUEST_URI'].'?';

			echo "<div class = '$this->filterTableCssClass' style = 'margin-bottom: 3px; '>";
			foreach ($filters[elements] as $element)
			{
				echo "<div style = 'border-bottom: 1px solid gray; border-collapse: collapse;'>";
					foreach ($element as $unit)
					{
						if ($current_controller->request->get[$filters[field]] == $unit) $unitStyle = 'background-color: orange';
						else $unitStyle = '';

						echo "<a href = '".$_SERVER['REQUEST_URI']."&$filters[field]=$unit'><div class = '{$this->filterAlphabeticUnit}' style = '$unitStyle'>$unit</div></a>";
					}
				echo "</div>";
			}

			if ($filters[has_show_all])
			{
				echo "<div style = 'text-align: left; padding: 1px; '>";
				    $this->cancel($_SERVER[REDIRECT_URL], ucfirst(LANG_SHOW_ALL), "class='$this->submitButtonCssClass'");
				echo "</div>";
			}

			echo "</div>";
		}

	    /**
		 * Generates table row for no records cases or errors
		 *
		 * @param string $colspan number of columns to be joined
		 * @param text $text text for output
		 * @param string $input_html_attributes html_attributes for the textarea field
		 * @param string $comment comment
		 */
		public function NoRecordsRow($colspan, $text=LANG_NO_RECORDS)
		{
		   	echo "
		  	  <tr>
		  	  	<td colspan='$colspan' class='$this->errorTableCellCssClass' align='center'>".ucfirst($text)."</td>
		  	  </tr>";
		}


		# BUTTONS #

	    /**
		 * Generates table with action buttons
		 *
		 * @param integer $controller_name name of the controller (default: $current_controller)
		 * @param integer $id record id
		 * @param integer $table_id table id
		 * @param depricated $show_permissions=true
  		 * @param boolean $child indicates whether the grid has hierarchical structure
		 */
		public function ActionButtonGroup($grid_settings, $id, $table_id, $child = 0)
		{
			global $current_controller, $object;

			//check if we use custom controller name for buttons or from the current controller
			if (empty($grid_settings[controller])) $controller_name = $current_controller->name;
			else $controller_name = $grid_settings[controller];

			echo "<table cellpading = '0' cellspacing = '0' style='float: left'><tr>";

			echo "<td>";
				$this->link("/".$controller_name.'/view'.$grid_settings[action].'/'.$id, '<img src="'.IMAGES.$this->viewImageName.'">', 'class="'.$this->actionButtonCssClass.'" id = "'.$grid_settings[name].'_view_'.$this->rowNumber.'" alt = "'.LANG_VIEW.'" title = "'.LANG_VIEW.'"');
			echo "</td>";
			echo "<td>";
				$this->link("/".$controller_name.'/edit'.$grid_settings[action].'/'.$id, '<img src="'.IMAGES.$this->editImageName.'">', 'class="'.$this->actionButtonCssClass.'" id = "'.$grid_settings[name].'_edit_'.$this->rowNumber.'" alt = "'.LANG_EDIT.'" title = "'.LANG_EDIT.'"');
			echo "</td>";
			echo "<td>";
				$this->link("/".$controller_name.'/delete'.$grid_settings[action].'/'.$id, '<img src="'.IMAGES.$this->deleteImageName.'">', 'class="'.$this->actionButtonCssClass.'" id = "'.$grid_settings[name].'_delete_'.$this->rowNumber.'" alt = "'.LANG_DELETE.'" title = "'.LANG_DELETE.'"');
			echo "</td>";
			if ($object->config->use_row_permissions)
			{
				echo "<td>";
					$this->link('/systempermissionsrows/edit/'.$table_id.'/'.$id, '<img src="'.IMAGES.$this->permissionImageName.'">', 'class="'.$this->actionButtonCssClass.'" id = "'.$grid_settings[name].'_permissions_'.$this->rowNumber.'" alt = "'.LANG_ACCESS.'" title = "'.LANG_ACCESS.'"');
				echo "</td>";
			}

			//if we deal with the hierarchical grid
			if (!empty($child))
			{
				echo "<td>";
					$this->link("/".$controller_name.'/add/'.$id, '<img src="'.IMAGES.$this->addChildImageName.'">', 'class="'.$this->actionButtonCssClass.'" id = "'.$grid_settings[name].'_add_child_'.$this->rowNumber.'"');
				echo "</td>";
				echo "<td>";
					$this->link("", '<img src="'.IMAGES.$this->cutImageName.'">',
						'onClick = "show_tags(\'\'); SetImagesActiveById(\'clipboard_paste_image\'); clipboard = '.$id.'; show_tags(\'a\'); document.getElementById(\'clipboard_paste_'.$id.'\').style.display = \'none\'; return false" class="'.$this->actionButtonCssClass.'"');
				echo "</td>";
				echo "<td>";
					$this->link('', '<img width = "14" height = "14" id = "clipboard_paste_image" src="'.IMAGES.$this->pasteInactiveImageName.'">',
						'id = "clipboard_paste_'.$id.'" onClick = "SendValuesWithPageReload(\'/'.$controller_name.'/ClipboardPaste/'.$id.'/\' + clipboard); return false;" class="'.$this->actionButtonCssClass.'"');
					echo "</td>";
			}

			echo "</tr></table>";
		}

		/**
		 * Delete button
		 *
		 * @param string $action action for the delete operation
		 */
	 	public function btnDelete($action)
        {
            global $current_controller;

            $this->link($action, '<img src="'.IMAGES.SEPARATOR.$this->deleteImageName.'">', 'class="'.$this->actionButtonCssClass.'"');
        }
	}
?>